#ifndef _CANARY2_SYS_POLLER_H_
#define _CANARY2_SYS_POLLER_H_

#include <chrono>
#include <vector>
#include <iterator>
#include <sys/epoll.h>
#include <array>
#include <ranges>
#include <span>

#ifndef _FD_GUARD_H_
#include "fd_guard.h"
#endif

namespace wilson::sys
{
    class poller
    {
    public:
        poller();
        void add(sys::borrowed_fd fd, uint32_t event);
        void mod(sys::borrowed_fd fd, uint32_t event);
        void del(sys::borrowed_fd fd);

        template<::std::output_iterator<epoll_event> Iter>
        void wait(Iter beg, ::std::chrono::milliseconds timeout);

    private:
        fd_guard p_epfd;
    };

    template<::std::output_iterator<epoll_event> BackInserter>
    void poller::wait(BackInserter beg, ::std::chrono::milliseconds timeout)
    {
        constexpr size_t epevs_size{ 512U };
        ::std::array<epoll_event, epevs_size> epevs;

        if (timeout.count() <= 0) [[unlikely]]
            timeout = ::std::chrono::seconds{ 1 };

        errno = 0;

        int ret = ::epoll_wait(
            p_epfd, 
            epevs.data(), epevs.max_size(), 
            timeout.count()
        );

        if (ret == -1)
        {
            // timeout
            if (errno == EINTR) [[likely]]
                return;
            
            // error
            throw ::std::runtime_error{ ::strerror(errno) };
        } 

        // Also timeout
        else if (ret == 0) return;

        ::std::span<const epoll_event> retspan{ 
            epevs.data(), 
            static_cast<size_t>(ret) 
        };
        auto ret_view = retspan
            | ::std::ranges::views::filter([](const auto& ev){
                  return ev.events != 0;
              });
        ::std::copy(ret_view.begin(), ret_view.end(), beg);
    }
}

#endif
